package com.haoqizhe.kernel.commons.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.haoqizhe.kernel.core.entity.TreeEntity;

import java.util.*;

/**
 * @author haoqizhe.li
 * @date 2018/6/26
 **/
public class TreeUtil {

    public static List<? extends TreeEntity> makeTree(List<? extends TreeEntity> nodes, String baseLevel)throws Exception{
        Map<String,List<TreeEntity>> nodesMap = getTreeMap(nodes);

        List<TreeEntity> rtn = nodesMap.get(baseLevel);
        if(rtn != null){
            for(Iterator<TreeEntity> it = rtn.iterator() ; it.hasNext(); ){
                TreeEntity t = it.next();
                recursiveTree(t, nodesMap);
            }
        }
        return rtn;
    }

    private static Map<String,List<TreeEntity>> getTreeMap(List<? extends TreeEntity> nodes){
        Map<String,List<TreeEntity>> nodesMap = new TreeMap<String,List<TreeEntity>>(new Comparator<String>() {
            @Override
            public int compare(String x, String y) {
                return x.compareTo(y);
            }
        });
        for(int i = 0 ; i < nodes.size() ; i ++){
            TreeEntity node = (TreeEntity)nodes.get(i);
            String level = node.getLevel();
            if(nodesMap.containsKey(node.getLevel())){
                nodesMap.get(level).add(node);
            }else{
                List<TreeEntity> levelNodes = new ArrayList<TreeEntity>();
                levelNodes.add(node);
                nodesMap.put(level, levelNodes);
            }
        }
        return nodesMap;
    }

    private static void recursiveTree(TreeEntity parentNode , Map<String,List<TreeEntity>> nodesMap){
        String parentLevel = parentNode.getLevel();
        String parentId = parentNode.getId();
        String childLevel = Integer.parseInt(parentLevel) + 1 + "";
        List<TreeEntity> levelNodes = nodesMap.get(childLevel);
        if(levelNodes != null){
            for(Iterator<TreeEntity> it = levelNodes.iterator() ; it.hasNext() ;){
                TreeEntity node = it.next();
                if(parentId.equals(node.getPid())){
                    if(parentNode.getChildren() == null){
                        parentNode.setChildren(new ArrayList<TreeEntity>());
                    }
                    parentNode.getChildren().add(node);
                    recursiveTree(node, nodesMap);
                }
            }
        }
    }
    /**
     * <p>设置树节点已选的数据</p>
     * @param nodes 树源数据
     * @param ids 已选择的树节点id列表
     * @param baseLevel 树根节点等级
     * @return  
     * @author haoqizhe.li
     * @date 2018/7/9
     **/
    public static List<? extends TreeEntity>  setChecked(List<? extends TreeEntity> nodes,List<String> ids,String baseLevel){
        Map<String,List<TreeEntity>> nodesMap = getTreeMap(nodes);
        List<TreeEntity> list = nodesMap.get(baseLevel);
        setYes(ids,list,true);
        return list;
    }
    private static void setYes(List<String> ids,List<TreeEntity> list,boolean isCheck){
        if(CollUtil.isNotEmpty(list) && CollUtil.isNotEmpty(ids)){
            String idsStr = "#"+String.join("#",ids)+"#";
            for(TreeEntity treeEntity : list){
                if(idsStr.contains("#"+treeEntity.getId()+"#")){
                    if(isCheck){
                        treeEntity.setChecked(true);
                    }else{
                        treeEntity.setState(true);
                    }
                }
                setYes(ids,treeEntity.getChildren(),isCheck);
            }
        }
    }

    /**
      * <p>设置树节点不可选数据状态</p>
      * @param nodes 树源数据
      * @param ids 可选的树节点id列表
      * @param baseLevel 树根节点等级
      * @return
      * @author haoqizhe.li
      * @date 2018/7/9
    **/
    public static List<? extends TreeEntity>  setState(List<? extends TreeEntity> nodes,List<String> ids,String baseLevel){
        Map<String,List<TreeEntity>> nodesMap = getTreeMap(nodes);
        List<TreeEntity> list = nodesMap.get(baseLevel);
        setYes(ids,list,false);
        return list;
    }


    public static List<? extends TreeEntity> setLevel(List<? extends TreeEntity> nodes){
        Map<String,TreeEntity> allNodesMap =new HashMap<>(nodes.size());
        for(TreeEntity treeEntity:nodes){
            if(treeEntity.getPid()==null){
                treeEntity.setLevel("0");
            }
            allNodesMap.put(treeEntity.getId(),treeEntity);
        }
        for(TreeEntity treeEntity:nodes){
            if(treeEntity.getLevel()==null){
                String pid =treeEntity.getPid();
                TreeEntity parent = allNodesMap.get(pid);
                if(parent!=null){
                    Integer level =Integer.parseInt(parent.getLevel())+1;
                    treeEntity.setLevel(level.toString());
                }
            }
        }
        return nodes;
    }

    /**
     * 根据parentId，构建树节点
     */
    public static <T extends TreeNode> List<T> build(List<T> treeNodes, Long parentId) {
        //pid不能为空
        Assert.isNull(parentId, "pid");

        List<T> treeList = new ArrayList<>();
        for(T treeNode : treeNodes) {
            if (parentId.equals(treeNode.getParentId())) {
                treeList.add(findChildren(treeNodes, treeNode));
            }
        }

        return treeList;
    }

    /**
     * 查找子节点
     */
    private static <T extends TreeNode> T findChildren(List<T> treeNodes, T rootNode) {
        for(T treeNode : treeNodes) {
            if(rootNode.getId().equals(treeNode.getParentId())) {
                rootNode.getChildren().add(findChildren(treeNodes, treeNode));
            }
        }
        return rootNode;
    }

    /**
     * 构建树节点
     */
    public static <T extends TreeNode> List<T> build(List<T> treeNodes) {
        List<T> result = new ArrayList<>();

        //list转map
        Map<Long, T> nodeMap = new LinkedHashMap<>(treeNodes.size());
        for(T treeNode : treeNodes){
            nodeMap.put(treeNode.getId(), treeNode);
        }

        for(T node : nodeMap.values()) {
            T parent = nodeMap.get(node.getParentId());
            if(parent != null && !(node.getId().equals(parent.getId()))){
                parent.getChildren().add(node);
                continue;
            }

            result.add(node);
        }

        return result;
    }

}
